﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.DTO;
using Common.DTO.ihopplockDTO;
using DataLayer;

namespace LogicLayer
{
    public class GameLobbyHandler
    {
        /// <summary>
        /// Creates a Game and 
        /// a PlayerGame is added.
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="gameTypeId"></param>
        /// <param name="rounds">number of rounds the player wants to play</param>
        /// <returns>boolean</returns>
        public static GameLobby CreateGame(int playerId, int gameTypeId, int rounds)
        {
            var repository = new RepositoryBase();
            //var player = GetDBInfo.GetPlayerById(playerId);
            var player = repository.GetEntityByID<PlayerEntity>(playerId);
            var gameType = GetDBInfo.GetGameTypeById(gameTypeId);

            int gameId = SaveDBInfo.CreateGame(gameType.ID, rounds);
            int gameRoundId = SaveDBInfo.CreateGameRound(gameId);
            int gamePlayerGameId = SaveDBInfo.CreatePlayerGame(gameId, player.ID);
            if (gamePlayerGameId > 0 && gameId > 0)
            {
                var game = GetDBInfo.GetGameById(gameId);
                var listOfPlayers = GetDBInfo.GetPlayersInGame(gameId);

                return new GameLobby
                {
                    GameID = gameId,
                    NameOfGame = game.NameOfGame,
                    NumOfGameRoundsToPlay = game.NumOfGameRoundsToPlay,
                    Players = listOfPlayers
                };
            }
            else
                return null;
        }

        public static bool DeleteGame(int gameId)
        {
            var repository = new RepositoryBase();
            var game = repository.GetEntityByID<GameEntity>(gameId);

            // Kolla så att spelet är GameState == Started
            var resultHandler = new ResultHandler();
            var gameState = resultHandler.DetermineGameState(game);
            if (gameState != GameState.Started)
            {
                return false;
            }

            return repository.Delete<GameEntity>(game);
        }

        public static bool UpdateGame(int gameId, int gameTypeId, int rounds)
        {
            var repository = new RepositoryBase();
            var game = repository.GetEntityByID<GameEntity>(gameId);

            // Kolla så att spelet är GameState == Started
            var resultHandler = new ResultHandler();
            var gameState = resultHandler.DetermineGameState(game);
            if (gameState != GameState.Started)
            {
                return false;
            }

            game.GameTypeID = gameTypeId;
            game.NoOfGameRounds = rounds;

            return repository.SaveChanges();
        }

        /// <summary>
        /// Removes a Player from a Game
        /// Deletes the row in table PlayerGame
        /// </summary>
        /// <param name="gameId">The gameID to remove from</param>
        /// <param name="playerId">The playerID to be removed</param>
        /// <returns>True if operation successful</returns>
        public static bool RemovePlayerFromGame(int gameId, int playerId)
        {
            var repository = new RepositoryBase();
            var playerInGame = repository.GetEntitiesWhere<PlayerGameEntity>(x => 
                                                                            x.PlayerID == playerId && 
                                                                            x.GameID == gameId);
            return repository.DeleteAll<PlayerGameEntity>(playerInGame);
        }

        

        public static List<GameType> GetGameTypeList()
        {
            var repository = new RepositoryBase();
            var gameTypes = repository.GetAll<GameTypeEntity>();

            return Factory.CreateListOfGameType(gameTypes);
        }

        public static List<Game> GetAllActiveGames()
        {
            var repository = new RepositoryBase();

            var games = repository.GetEntitiesWhere<GameEntity>(x =>
                                                                    x.ActiveGame == true &&
                                                                    x.Winner == null).ToList();
            return games.Select(x =>
                new Game
                {
                    ID = x.ID,
                    GameTypeID = x.GameTypeID,
                    NameOfGame = x.GameType.Name,
                    NumOfGameRoundsToPlay = x.NoOfGameRounds,
                }).ToList();
        }



        public static List<JoinableGame> GetAllJoinableGames(int gameTypeID, int playerId)
        {
            var repository = new RepositoryBase();

            var games = repository.GetEntitiesWhere<GameEntity>(x =>
                                                                    x.ActiveGame == false &&
                                                                    x.Winner == null &&
                                                                    x.GameTypeID == gameTypeID &&
                                                                    x.PlayerGame.Any(p => p.PlayerID != playerId)).ToList();

            return games.Select(x =>
                new JoinableGame
                {
                    GameID = x.ID,
                    NameOfGame = x.GameType.Name,
                    NumOfGameRoundsToPlay = x.NoOfGameRounds,
                    StartDate = x.DateStart,
                    Participants = x.PlayerGame.Select(p => Factory.CreatePlayer(p.Player)).ToList()
                }).ToList();
        }

        public static List<GameNotStarted> GetAllGamesNotStartedWithPlayer(int playerAskingId)
        {
            var repository = new RepositoryBase();

            var games = repository.GetEntitiesWhere<GameEntity>(x =>
                                                                    x.ActiveGame == false &&
                                                                    x.Winner == null &&
                                                                    x.PlayerGame.Any(p => p.PlayerID == playerAskingId)).ToList();

            return games.Select(x =>
                new GameNotStarted
                {
                    GameID = x.ID,
                    NameOfGame = x.GameType.Name,
                    NumOfGameRoundsToPlay = x.NoOfGameRounds,
                    StartDate = x.DateStart,
                    Participants = x.PlayerGame.Select(p => Factory.CreatePlayer(p.Player)).ToList()
                }).ToList();
        }


        ///// <summary>
        ///// Hämtar en spelares alla Games.... importante
        ///// </summary>
        ///// <param name="playerID"></param>
        ///// <returns></returns>
        //public static List<Game> GetPlayerGames(int playerID)
        //{
        //    var games = GetDBInfo.GetPlayersGames(playerID);

        //    return
        //        games.Select(
        //            g =>
        //            new Game
        //            {
        //                ActiveGame = true,
        //                ID = g.ID,
        //                GameTypeID = g.GameTypeID,
        //                NameOfGame = GetDBInfo.GetGameTypeById(g.GameTypeID).Name,
        //                NumOfGameRoundsToPlay = g.NoOfGameRounds
        //            }).ToList();
        //}





        public static bool JoinGame(int gameId, int playerId)
        {
            var repository = new RepositoryBase();

            var playerGame = new PlayerGameEntity
            {
                GameID = gameId,
                PlayerID = playerId
            };
            repository.Save<PlayerGameEntity>(playerGame);

            var game = repository.GetEntityByID<GameEntity>(gameId);
            if (game.PlayerGame.Count == 2) // FUTURE: Ändra för godtyckligt antal spelare (kräver DB uppdatering)
            {
                game.ActiveGame = true;
                repository.SaveChanges();
            }

            return true;
        }

        public static bool JoinRandomGame(int playerId, int gameTypeId)
        {
            var repository = new RepositoryBase();

            var games = repository.GetEntitiesWhere<GameEntity>(x =>
                                                                    x.ActiveGame == false &&
                                                                    x.Winner == null &&
                                                                    x.PlayerGame.Any(p => p.PlayerID != playerId)).ToList();
            if (games.Count == 0)
            {
                return false;
            }
            
            // FUTURE: Gör smartare slumpval
            var random = new Random();
            var gameIndex = random.Next(games.Count);
            var game = games[gameIndex];

            return JoinGame(game.ID, playerId);
        }

        //public static GameLobby GetGameLobbyStatus(int gameId)
        //{
        //    var game = GetDBInfo.GetGameById(gameId);
        //    var listOfPlayers = GetDBInfo.GetPlayersInGame(gameId);

        //    return new GameLobby
        //    {
        //        GameID = gameId,
        //        NameOfGame = game.NameOfGame,
        //        NumOfGameRoundsToPlay = game.NumOfGameRoundsToPlay,
        //        Players = listOfPlayers
        //    };
        //}

        //public static List<Game> GetPlayersActiveGames(int playerId, int gameTypeId)
        //{
        //    return GetDBInfo.GetPlayersActiveGames(playerId, gameTypeId)
        //        .Select(g => new Game
        //        {
        //            ActiveGame = true,
        //            ID = g.ID,
        //            GameTypeID = g.GameTypeID,
        //            NameOfGame = GetDBInfo.GetGameTypeById(g.GameTypeID).Name,
        //            NumOfGameRoundsToPlay = g.NoOfGameRounds
        //        }).ToList();
        //}

        /// <summary>
        /// Return a list with the Player's active Games and information about them
        /// </summary>
        /// <param name="playerAskingID">The Player asking ID</param>
        /// <param name="gameTypeId">The ID of the GameType</param>
        /// <returns>List of the Player's active games</returns>
        public static List<PlayerActiveGame_Init> GetPlayersActiveGameList_Init(int playerAskingID, int gameTypeId)
        {
            var repository = new RepositoryBase();

            var games = repository.GetEntitiesWhere<GameEntity>(x =>
                                                                    x.ActiveGame == true &&
                                                                    x.Winner == null &&
                                                                    x.PlayerGame.Any(p => p.PlayerID == playerAskingID) &&
                                                                    x.GameTypeID == gameTypeId).ToList();

            var resultHandler = new ResultHandler();
            var activeGames = new List<PlayerActiveGame_Init>();
            foreach (var game in games)
            {
                activeGames.Add(new PlayerActiveGame_Init
                {
                    GameID = game.ID,
                    TotalGameRounds = game.NoOfGameRounds,
                    ActiveGameRound = game.GameRound.Count,
                    Opponent = GetOpponent(game, playerAskingID),
                    PlayerTurn = resultHandler.DeterminePlayerTurn(game, playerAskingID)
                });
            }

            return activeGames;
        }

        public static List<PlayerActiveGame_Poll> GetPlayersActiveGameList_Poll(int playerAskingID, int gameTypeId)
        {
            var repository = new RepositoryBase();

            var games = repository.GetEntitiesWhere<GameEntity>(x =>
                                                                    x.ActiveGame == true &&
                                                                    x.Winner == null &&
                                                                    x.PlayerGame.Any(p => p.PlayerID == playerAskingID) &&
                                                                    x.GameTypeID == gameTypeId).ToList();

            var resultHandler = new ResultHandler();
            var activeGames = new List<PlayerActiveGame_Poll>();
            foreach (var game in games)
            {
                activeGames.Add(new PlayerActiveGame_Poll
                {
                    GameID = game.ID,
                    ActiveGameRound = game.GameRound.Count,
                    PlayerTurn = resultHandler.DeterminePlayerTurn(game, playerAskingID)
                });
            }

            return activeGames;
        }


        private static Player GetOpponent(GameEntity game, int playerAskingId)
        {
            var repository = new RepositoryBase();

            foreach (var player in game.PlayerGame)
            {
                if (player.PlayerID != playerAskingId)
                {
                    return Factory.CreatePlayer(player.Player);
                }
            }

            return null;
        }



        
    }
}
